import std.file, std.stdio;
import std.exception, std.string, std.conv;
static import std.regex;
static import std.random;

static import irc;
static import config;
static import maps;

string owner_nick = "";

pure string replyTo( string caller, string target )
{
	if ( (target.length > 0) && (target[0] == '#') )
		return target;
	else
		return caller;
}

// Callbacks
void quit( ref irc.IRCClient client, string caller, string target, string line )
{
	if ( caller == owner_nick )
	{
		writeln( "Quit command issued from valid authority" );
		client.disconnect();
	}
}

void ownerAuth( ref irc.IRCClient client, string caller, string target, string line )
{
	try
	{
		if ( !target.length )
			return;
		
		if ( target[0] == '#' )
		{
			client.sendPrivMsg( target, "It is not a good idea to authorize in public channel" );
			return;
		}

		string password = config.get!(string)("rpg/owner_password");
		if ( line == ("!authorize "~password) )
		{
			owner_nick = caller;
			client.sendPrivMsg( replyTo( caller, target) , "Authorization success" );
		}
		else
			client.sendPrivMsg( replyTo( caller, target),  "Authorization failure" );
	}
	catch ( Exception e )
	{
		client.sendPrivMsg( replyTo( caller, target),  "Authorization function failed, sorry" );
		writeln( "ownerAuth() failed: " ~ e.msg );
	}
}

void roll( ref irc.IRCClient client, string caller, string target, string line )
{
	class DiceLimit : Exception
	{
		public:

		this( string msg ) 
		{
			super( msg );
		}
	}

	try
	{ 
		auto rgxp_dices = std.regexp.RegExp("([+-]?)(\\d+)d(\\d+)");
		auto rgxp_dice_tail = std.regexp.RegExp("^([+-])(\\d+)");

		bool explicit = ( line.length >= 6 ) &&( line[0..6] == "!roll " );

		if (explicit)
			line = line[6..$];

		auto sum = 0;
		string comment;

		foreach ( m; rgxp_dices.search(line) )
		{
			auto d = to!long( m[3] );
			auto count = to!long( m[2] );
			
			long limit = config.get!long("rpg/dice_limit");
			if ( count > limit )
				throw new DiceLimit( m[2] ~ " exceeds dice limit of " ~ to!string(limit) ); 

			comment ~= "[ ";

			for ( auto i = 0; i < count; i++ )
			{			
				auto roll = std.random.uniform( 1, d+1 );
				comment ~= to!string(roll)~" ";
				sum += roll * ( m[1] == "-" ? -1 : 1 );
			}

			comment ~= "] ";

			line = m.post;
		}

		foreach ( m; rgxp_dice_tail.search(line) )
		{
			sum += to!long(m[2]) * ( m[1][0] == '-' ? -1 : 1 );
			line = m.post;
		}

		line = line.strip;

		if (line.length)
			comment = line ~ ": " ~ to!string(sum) ~ " " ~ comment;
		else
			comment = to!string(sum) ~ " " ~ comment; 

		comment = caller ~ ", " ~ comment;

		client.sendPrivMsg( replyTo( caller, target ), comment ); 
	}
	catch ( std.conv.ConvError e )
	{
		client.sendPrivMsg( replyTo( caller, target ), caller ~ ", range violation will not be tolerated" ); 
		writeln( "roll() failed: " ~ e.msg );
	}
	catch ( std.regexp.RegExpException e )
	{
		client.sendPrivMsg( replyTo( caller, target ), "Regexp failure while rolling" ); 
		writeln( "roll() failed: " ~ e.msg );
	}
	catch ( DiceLimit e )
	{
		client.sendPrivMsg( replyTo( caller, target ), e.msg ); 
	}
	catch ( Exception e )
	{
		client.sendPrivMsg( replyTo( caller, target ), "Unrecognized failure while rolling" ); 
		writeln( "roll() failed: " ~ e.msg );
	} 
}


int main()
{
	irc.IRCClient client;

	try
	{
		// Loading configuration
		writeln("Initialiazing configuration...");
		config.initialize( readText("config.json") );

		string addr = config.get!(string)("irc/server");
		long port = config.get!(long)("irc/port");
		enforce( port >= 0 );
		string name = config.get!(string)("irc/name");
		string pass = config.get!(string)("irc/password");	
	
		writeln("Registering command handlers...");
		client.addHandler( "^!quit$", &quit );
		client.addHandler( "^!authorize ", &ownerAuth );
		client.addHandler( "^!roll .+", &roll);
		client.addHandler( "^\\d+d\\d+.*", &roll);

		// Initiazaling IRC client
		writeln("Connecting to IRC server...");
		client.connect( addr, cast(ushort)port, name, pass );

		string channels = config.get!(string)("irc/channels");
		writeln( "Joining ", channels );
		client.join( channels.split( ","  ) );

		writeln( "Initialization done. Runnning." );
	}
	catch (Exception e)
	{
		writeln( "Bot starting terminated due to exception:" );
		writeln( "\t", e.msg );
		return 1;
	}
	catch( Throwable t )
	{
		writeln( "Bot starting terminated abnormally." );
		writeln( "\t", t.msg);
		return 1;
	}

	try
	{
		client.run();
	}
	catch (Exception e)
	{
		writeln("Bot terminated unexpectedly due to exception:");
		writeln("\t", e.msg);
		return 1;
	}
	catch (Throwable t)
	{
		writeln( "Bot terminated abnormally." );
		writeln( "\t", t.msg);
		return 1;
	}

	return 0;
}
